package com.yc.system.service.impl;

import com.yc.common.annotation.Log;
import com.yc.common.enums.OperationType;
import com.yc.framework.config.IpLimitConfig;
import com.yc.common.core.domain.entity.SysUser;
import com.yc.system.mapper.SysUserMapper;
import com.yc.system.service.IpLimitService;
import com.yc.common.utils.ip.AddressUtil;
import com.yc.common.utils.ip.IpUtil;
import com.yc.common.core.redis.RedisCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

@Service
public class IpLimitServiceImpl implements IpLimitService {

    private static final Logger logger = LoggerFactory.getLogger(IpLimitServiceImpl.class);

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IpLimitConfig ipLimitConfig;

    @Autowired
    private SysUserMapper userMapper;

    private static final String IP_COUNT_KEY = "ip:count:";
    private static final String USER_VIOLATION_KEY = "user:violation:";
    private static final String USER_BAN_KEY = "user:ban:";
    private static final String IP_LOCATION_KEY = "ip:location:";
    private static final String IP_BAN_KEY = "ip:ban:";
    private static final String IP_VIOLATION_KEY = "ip:violation:";

    @Override
    public boolean checkIpLimit(HttpServletRequest request, boolean isLoginRequest) {
        String ip = IpUtil.getIpAddr(request);
        String uri = request.getRequestURI();
        String key = IP_COUNT_KEY + ip + ":" + uri;

        // 获取当前访问次数
        Integer count = redisCache.getCacheObject(key);
        if (count == null) {
            // 首次访问，设置计数为1，过期时间为1分钟
            redisCache.setCacheObject(key, 1, 1L, TimeUnit.MINUTES);
            return true;
        }

        // 判断是否超过限制
        int maxRequests = isLoginRequest ?
                ipLimitConfig.getMaxLoginRequestPerMinute() :
                ipLimitConfig.getMaxRequestPerMinute();

        if (count >= maxRequests) {
            logger.warn("IP: {} 访问 {} 超过限制: {}/{} 次/分钟",
                    ip, uri, count, maxRequests);
            return false;
        }

        // 增加计数 - 使用获取并更新的方式替代increment
        redisCache.setCacheObject(key, count + 1, 1L, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public void recordAccess(HttpServletRequest request, Long userId, String uri) {
        String ip = IpUtil.getIpAddr(request);

        // 记录IP地址的地理位置（缓存1天）
        if (redisCache.getCacheObject(IP_LOCATION_KEY + ip) == null) {
            String location = AddressUtil.getRealAddressByIP(ip);
            logger.debug("记录IP地址位置 - IP: {}, 位置: {}", ip, location);
            redisCache.setCacheObject(IP_LOCATION_KEY + ip, location, 1, TimeUnit.DAYS);
        }

        // 如果是已登录用户且访问被限制
        if (userId != null) {
            String key = IP_COUNT_KEY + ip + ":" + uri;
            Integer count = redisCache.getCacheObject(key);

            if (count != null && count >= ipLimitConfig.getMaxRequestPerMinute()) {
                // 记录违规次数
                String violationKey = USER_VIOLATION_KEY + userId;
                Integer violations = redisCache.getCacheObject(violationKey);

                if (violations == null) {
                    redisCache.setCacheObject(violationKey, 1, 1L, TimeUnit.DAYS);
                } else {
                    // 增加违规计数
                    redisCache.setCacheObject(violationKey, violations + 1, 1L, TimeUnit.DAYS);

                    // 检查是否需要禁用用户
                    if (violations + 1 >= ipLimitConfig.getMaxViolationCount()) {
                        banUser(userId);
                    }
                }
            }
        }
    }

    @Override
    public boolean isUserBanned(Long userId) {
        return redisCache.getCacheObject(USER_BAN_KEY + userId) != null;
    }

    @Override
    public boolean shouldBanUser(Long userId) {
        if (userId == null) {
            return false;
        }

        // 获取用户违规次数
        String violationKey = USER_VIOLATION_KEY + userId;
        Integer violations = redisCache.getCacheObject(violationKey);

        // 如果违规次数达到或超过阈值，则应该禁用用户
        return violations != null && violations >= ipLimitConfig.getMaxViolationCount();
    }

    @Log(module = "访问限制", type = OperationType.ERROR, description = "用户访问限制")
    @Override
    public void banUser(Long userId) {
        // 在Redis中标记用户被禁用
        redisCache.setCacheObject(
                USER_BAN_KEY + userId,
                LocalDateTime.now().toString(),
                ipLimitConfig.getBanMinutes(),
                TimeUnit.MINUTES);

        // 更新数据库中的用户状态为停用(status=1)
        try {
            SysUser user = userMapper.selectById(userId);
            if (user != null) {
                user.setStatus("1"); // 设置为停用状态
                userMapper.updateById(user);
                logger.warn("用户ID: {} 因频繁访问被禁用 {} 分钟", userId, ipLimitConfig.getBanMinutes());
            }
        } catch (Exception e) {
            logger.error("禁用用户失败: {}", e.getMessage());
        }
    }

    @Override
    public boolean isIpBanned(String ipAddress) {
        return redisCache.getCacheObject(IP_BAN_KEY + ipAddress) != null;
    }

    @Override
    public void recordIpViolation(String ipAddress) {
        String violationKey = IP_VIOLATION_KEY + ipAddress;
        Integer violations = redisCache.getCacheObject(violationKey);

        if (violations == null) {
            redisCache.setCacheObject(violationKey, 1, 1L, TimeUnit.DAYS);
        } else {
            // 增加违规计数
            int newCount = violations + 1;
            redisCache.setCacheObject(violationKey, newCount, 1L, TimeUnit.DAYS);

            logger.warn("IP违规计数增加 - IP: {}, 当前违规次数: {}/{}",
                    ipAddress, newCount, ipLimitConfig.getMaxViolationCount());

            // 如果超过配置的违规次数，禁止该IP
            if (newCount >= ipLimitConfig.getMaxViolationCount()) {
                banIp(ipAddress);
            }
        }
    }

    /**
     * 禁止IP访问
     */
    private void banIp(String ipAddress) {
        // 在Redis中标记IP被禁用
        redisCache.setCacheObject(
                IP_BAN_KEY + ipAddress,
                LocalDateTime.now().toString(),
                ipLimitConfig.getBanMinutes(),
                TimeUnit.MINUTES);

        // 重置违规计数
        redisCache.deleteObject(IP_VIOLATION_KEY + ipAddress);

        logger.warn("IP已被禁止访问 - IP: {}, 禁止时长: {} 分钟",
                ipAddress, ipLimitConfig.getBanMinutes());
    }
}